#ifndef _TBMAT3X3_H_
#define _TBMAT3X3_H_

#include "TBSettings.h"

#include "TBMath.h"

namespace tb
{

	class Vec2;
	class Vec3;
	class Mat4x4;

	class TB_DLL_FUNC Mat3x3
	{

	public:

		enum Field
		{
			X1 = 0,  Y1 = 1,  WX = 2,
			X2 = 3,  Y2 = 4,  WY = 5,
			TX = 6,  TY = 7,  CZ = 8,

			SX = 0,
			         SY = 4
		};

		// ================================
		// Constructors
		// ================================

		Mat3x3();
		Mat3x3(
			float a_X1, float a_Y1, float a_WX,
			float a_X2, float a_Y2, float a_WY,
			float a_TX, float a_TY, float a_CZ
		);
		Mat3x3(const Mat3x3& a_Other);
		~Mat3x3();

		// ================================
		// Conversion
		// ================================

		Mat3x3& FromEulerAngle(float a_Degrees);
		Mat3x3& FromOrthographic(float a_Left, float a_Right, float a_Top, float a_Bottom);

		Mat3x3& Decompose(Vec2& a_Translation, float& a_Rotation, Vec2& a_Scale);

		// ================================
		// Assignment & Access
		// ================================

		Mat3x3& operator = (const float* a_Values);
		Mat3x3& operator = (const Mat3x3& a_Other);
		Mat3x3& operator = (const Mat4x4& a_Other);

		float& operator [] (unsigned int a_Index);
		operator float* ();

		Vec3 GetColumn(int a_Column);
		Vec3 GetRow(int a_Row);

		// ================================
		// Operations
		// ================================

		Vec2 operator * (const Vec2& a_Vector) const;
		Vec3 operator * (const Vec3& a_Vector) const;

		Mat3x3 operator + (const Mat3x3& a_Other) const;
		Mat3x3& operator += (const Mat3x3& a_Other);

		Mat3x3 operator * (const Mat3x3& a_Other) const;
		Mat3x3& operator *= (const Mat3x3& a_Other);

		Mat3x3 operator * (float a_Value) const;
		Mat3x3& operator *= (float a_Value);

		Mat3x3 operator / (float a_Value) const;
		Mat3x3& operator /= (float a_Value);

		// ================================
		// Comparison
		// ================================

		bool operator == (const Mat3x3& a_Other) const;
		bool operator == (float* a_Values) const;

		bool operator != (const Mat3x3& a_Other) const;

		bool Compare(const Mat3x3& a_Other, float a_Delta = 1e-6f) const;
		bool Compare(float* a_Values, float a_Delta = 1e-6f) const;

		bool IsIdentity(float a_Delta = 1e-6f) const;

		// ================================
		// Functions
		// ================================

		//! Clears the values of this Mat3x3.
		Mat3x3& Clear();

		//! Sets this Mat3x3 to the identity matrix.
		Mat3x3& Identity();

		Mat3x3& Transpose();
		Mat3x3 GetTransposed() const;

		//! Inverts this Mat3x3.
		Mat3x3& Invert();
		//! Returns the inverted version of this Mat3x3.
		Mat3x3 GetInverted() const;

		Vec2 Transform(const Vec2& a_Vector);
		Vec2 TransformRotation(const Vec2& a_Vector);

		// ================================
		// Rotation
		// ================================

		float GetRotation() const;
		Mat3x3& AddRotation(float a_Degrees);
		Mat3x3& SetRotation(float a_Degrees);

		// ================================
		// Translation
		// ================================

		Vec2 GetTranslation() const;
		Mat3x3& AddTranslation(const Vec2& a_Translation);
		Mat3x3& AddTranslation(float a_X, float a_Y);
		Mat3x3& SetTranslation(const Vec2& a_Translation);
		Mat3x3& SetTranslation(float a_X, float a_Y);
		Mat3x3& SetTranslationTransformed(const Vec2& a_Translation);
		Mat3x3& SetTranslationTransformed(float a_X, float a_Y);

		// ================================
		// Scale
		// ================================

		Vec2 GetScale() const;
		Mat3x3& AddScale(const Vec2& a_Scale);
		Mat3x3& AddScale(float a_Scale);
		Mat3x3& SetScale(const Vec2& a_Scale);
		Mat3x3& SetScale(float a_Scale);

		// ================================
		// Data
		// ================================

		float values[9];

	}; // class Mat3x3

}; // namespace tb

#include "TBMat3x3.inl"

#endif